package com.github.gexiao.middleware.whitelist;

import com.github.gexiao.middleware.whitelist.annotation.DoWhiteList;
import com.github.gexiao.middleware.whitelist.exception.AccessRejectException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;

@Aspect
public class DoWhiteListAspect {
    private final Logger logger = LoggerFactory.getLogger(DoWhiteListAspect.class);

    @Resource
    private String whiteListConfig;

    @Pointcut("@annotation(com.github.gexiao.middleware.whitelist.annotation.DoWhiteList)")
    public void aopPoint() {

    }

    @Around("aopPoint()")
    public Object doRouter(ProceedingJoinPoint pjp) throws Throwable {
        Method method = getMethod(pjp);
        DoWhiteList whiteList = method.getAnnotation(DoWhiteList.class);

        String keyValue = getKeyValue(whiteList.key(), pjp, whiteList.location());
        logger.info("middleware whitelist handler method：{} value：{}", method.getName(), keyValue);
        if (!StringUtils.hasText(keyValue)) return pjp.proceed();

        // 白名单过滤
        boolean match = Arrays.asList(whiteListConfig.split(",")).contains(keyValue);
        if (match) return pjp.proceed();

        throw new AccessRejectException(whiteList.msg() + ": " + keyValue);
    }

    private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
        Signature sig = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) sig;
        return jp.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
    }


    // 获取属性值
    private String getKeyValue(String filed, ProceedingJoinPoint pjp, Location location) {
        String filedValue = null;
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        switch (location) {
            case parameter:
                String[] parameterNames = ((MethodSignature) pjp.getSignature()).getParameterNames();
                for (int i = 0; i < parameterNames.length; i++) {
                    if (filed.equals(parameterNames[i])) {
                        Object[] args = pjp.getArgs();
                        filedValue = (String) args[i];
                    }
                }
                break;
            case path:
                Map attribute = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
                filedValue = (String) attribute.get(filed);
                break;
            case header:
                filedValue = request.getHeader(filed);
                break;
        }
        return filedValue;
    }

}
